<?php

if (! defined ( 'BASEPATH' ))
	exit ( 'No direct script access allowed' );
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------


/**
 * FTP Class
 *
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @category	Libraries
 * @author		ExpressionEngine Dev Team
 * @link		http://codeigniter.com/user_guide/libraries/ftp.html
 */
class CI_FTP {
	
	var $hostname = '';
	var $username = '';
	var $password = '';
	var $port = 21;
	var $passive = TRUE;
	var $debug = FALSE;
	var $conn_id = FALSE;
	
	/**
	 * Constructor - Sets Preferences
	 *
	 * The constructor can be passed an array of config values
	 */
	public function __construct($config = array()) {
		if (count ( $config ) > 0) {
			$this->initialize ( $config );
		}
		
		log_message ( 'debug', "FTP Class Initialized" );
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Initialize preferences
	 *
	 * @access	public
	 * @param	array
	 * @return	void
	 */
	function initialize($config = array()) {
		foreach ( $config as $key => $val ) {
			if (isset ( $this->$key )) {
				$this->$key = $val;
			}
		}
		
		// Prep the hostname
		$this->hostname = preg_replace ( '|.+?://|', '', $this->hostname );
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * FTP Connect
	 *
	 * @access	public
	 * @param	array	 the connection values
	 * @return	bool
	 */
	function connect($config = array()) {
		if (count ( $config ) > 0) {
			$this->initialize ( $config );
		}
		
		if (FALSE === ($this->conn_id = @ftp_connect ( $this->hostname, $this->port ))) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_connect' );
			}
			return FALSE;
		}
		
		if (! $this->_login ()) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_login' );
			}
			return FALSE;
		}
		
		// Set passive mode if needed
		if ($this->passive == TRUE) {
			ftp_pasv ( $this->conn_id, TRUE );
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * FTP Login
	 *
	 * @access	private
	 * @return	bool
	 */
	function _login() {
		return @ftp_login ( $this->conn_id, $this->username, $this->password );
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Validates the connection ID
	 *
	 * @access	private
	 * @return	bool
	 */
	function _is_conn() {
		if (! is_resource ( $this->conn_id )) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_no_connection' );
			}
			return FALSE;
		}
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Change directory
	 *
	 * The second parameter lets us momentarily turn off debugging so that
	 * this function can be used to test for the existence of a folder
	 * without throwing an error.  There's no FTP equivalent to is_dir()
	 * so we do it by trying to change to a particular directory.
	 * Internally, this parameter is only used by the "mirror" function below.
	 *
	 * @access	public
	 * @param	string
	 * @param	bool
	 * @return	bool
	 */
	function changedir($path = '', $supress_debug = FALSE) {
		if ($path == '' or ! $this->_is_conn ()) {
			return FALSE;
		}
		
		$result = @ftp_chdir ( $this->conn_id, $path );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE and $supress_debug == FALSE) {
				$this->_error ( 'ftp_unable_to_changedir' );
			}
			return FALSE;
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Create a directory
	 *
	 * @access	public
	 * @param	string
	 * @return	bool
	 */
	function mkdir($path = '', $permissions = NULL) {
		if ($path == '' or ! $this->_is_conn ()) {
			return FALSE;
		}
		
		$result = @ftp_mkdir ( $this->conn_id, $path );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_makdir' );
			}
			return FALSE;
		}
		
		// Set file permissions if needed
		if (! is_null ( $permissions )) {
			$this->chmod ( $path, ( int ) $permissions );
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Upload a file to the server
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL) {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		if (! file_exists ( $locpath )) {
			$this->_error ( 'ftp_no_source_file' );
			return FALSE;
		}
		
		// Set the mode if not specified
		if ($mode == 'auto') {
			// Get the file extension so we can set the upload type
			$ext = $this->_getext ( $locpath );
			$mode = $this->_settype ( $ext );
		}
		
		$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
		
		$result = @ftp_put ( $this->conn_id, $rempath, $locpath, $mode );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_upload' );
			}
			return FALSE;
		}
		
		// Set file permissions if needed
		if (! is_null ( $permissions )) {
			$this->chmod ( $rempath, ( int ) $permissions );
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Download a file from a remote server to the local server
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function download($rempath, $locpath, $mode = 'auto') {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		// Set the mode if not specified
		if ($mode == 'auto') {
			// Get the file extension so we can set the upload type
			$ext = $this->_getext ( $rempath );
			$mode = $this->_settype ( $ext );
		}
		
		$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
		
		$result = @ftp_get ( $this->conn_id, $locpath, $rempath, $mode );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_download' );
			}
			return FALSE;
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Rename (or move) a file
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @param	bool
	 * @return	bool
	 */
	function rename($old_file, $new_file, $move = FALSE) {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		$result = @ftp_rename ( $this->conn_id, $old_file, $new_file );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$msg = ($move == FALSE) ? 'ftp_unable_to_rename' : 'ftp_unable_to_move';
				
				$this->_error ( $msg );
			}
			return FALSE;
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Move a file
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @return	bool
	 */
	function move($old_file, $new_file) {
		return $this->rename ( $old_file, $new_file, TRUE );
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Rename (or move) a file
	 *
	 * @access	public
	 * @param	string
	 * @return	bool
	 */
	function delete_file($filepath) {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		$result = @ftp_delete ( $this->conn_id, $filepath );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_delete' );
			}
			return FALSE;
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Delete a folder and recursively delete everything (including sub-folders)
	 * containted within it.
	 *
	 * @access	public
	 * @param	string
	 * @return	bool
	 */
	function delete_dir($filepath) {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		// Add a trailing slash to the file path if needed
		$filepath = preg_replace ( "/(.+?)\/*$/", "\\1/", $filepath );
		
		$list = $this->list_files ( $filepath );
		
		if ($list !== FALSE and count ( $list ) > 0) {
			foreach ( $list as $item ) {
				// If we can't delete the item it's probaly a folder so
				// we'll recursively call delete_dir()
				if (! @ftp_delete ( $this->conn_id, $item )) {
					$this->delete_dir ( $item );
				}
			}
		}
		
		$result = @ftp_rmdir ( $this->conn_id, $filepath );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_delete' );
			}
			return FALSE;
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Set file permissions
	 *
	 * @access	public
	 * @param	string	the file path
	 * @param	string	the permissions
	 * @return	bool
	 */
	function chmod($path, $perm) {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		// Permissions can only be set when running PHP 5
		if (! function_exists ( 'ftp_chmod' )) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_chmod' );
			}
			return FALSE;
		}
		
		$result = @ftp_chmod ( $this->conn_id, $perm, $path );
		
		if ($result === FALSE) {
			if ($this->debug == TRUE) {
				$this->_error ( 'ftp_unable_to_chmod' );
			}
			return FALSE;
		}
		
		return TRUE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * FTP List files in the specified directory
	 *
	 * @access	public
	 * @return	array
	 */
	function list_files($path = '.') {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		return ftp_nlist ( $this->conn_id, $path );
	}
	
	// ------------------------------------------------------------------------
	

	/**
	 * Read a directory and recreate it remotely
	 *
	 * This function recursively reads a folder and everything it contains (including
	 * sub-folders) and creates a mirror via FTP based on it.  Whatever the directory structure
	 * of the original file path will be recreated on the server.
	 *
	 * @access	public
	 * @param	string	path to source with trailing slash
	 * @param	string	path to destination - include the base folder with trailing slash
	 * @return	bool
	 */
	function mirror($locpath, $rempath) {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		// Open the local file path
		if ($fp = @opendir ( $locpath )) {
			// Attempt to open the remote file path.
			if (! $this->changedir ( $rempath, TRUE )) {
				// If it doesn't exist we'll attempt to create the direcotory
				if (! $this->mkdir ( $rempath ) or ! $this->changedir ( $rempath )) {
					return FALSE;
				}
			}
			
			// Recursively read the local directory
			while ( FALSE !== ($file = readdir ( $fp )) ) {
				if (@is_dir ( $locpath . $file ) && substr ( $file, 0, 1 ) != '.') {
					$this->mirror ( $locpath . $file . "/", $rempath . $file . "/" );
				} elseif (substr ( $file, 0, 1 ) != ".") {
					// Get the file extension so we can se the upload type
					$ext = $this->_getext ( $file );
					$mode = $this->_settype ( $ext );
					
					$this->upload ( $locpath . $file, $rempath . $file, $mode );
				}
			}
			return TRUE;
		}
		
		return FALSE;
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Extract the file extension
	 *
	 * @access	private
	 * @param	string
	 * @return	string
	 */
	function _getext($filename) {
		if (FALSE === strpos ( $filename, '.' )) {
			return 'txt';
		}
		
		$x = explode ( '.', $filename );
		return end ( $x );
	}
	
	// --------------------------------------------------------------------
	

	/**
	 * Set the upload type
	 *
	 * @access	private
	 * @param	string
	 * @return	string
	 */
	function _settype($ext) {
		$text_types = array ('txt', 'text', 'php', 'phps', 'php4', 'js', 'css', 'htm', 'html', 'phtml', 'shtml', 'log', 'xml' );
		
		return (in_array ( $ext, $text_types )) ? 'ascii' : 'binary';
	}
	
	// ------------------------------------------------------------------------
	

	/**
	 * Close the connection
	 *
	 * @access	public
	 * @param	string	path to source
	 * @param	string	path to destination
	 * @return	bool
	 */
	function close() {
		if (! $this->_is_conn ()) {
			return FALSE;
		}
		
		@ftp_close ( $this->conn_id );
	}
	
	// ------------------------------------------------------------------------
	

	/**
	 * Display error message
	 *
	 * @access	private
	 * @param	string
	 * @return	bool
	 */
	function _error($line) {
		$CI = & get_instance ();
		$CI->lang->load ( 'ftp' );
		show_error ( $CI->lang->line ( $line ) );
	}

}
// END FTP Class

/* End of file Ftp.php */
/* Location: ./system/libraries/Ftp.php */